﻿#region License
// Copyright (c) 2009 Shengmin Zhang
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#endregion

using System;
using System.IO;
using System.Text;
using System.Reflection;
using System.Collections.Generic;

using Apprender.Newtonsoft.Json;
using Apprender.Extensions.Methods;

namespace Apprender.Serialization.Json
{
    public class JSerializer : IJsonSerializer, IJsonParameterSerializer
    {
        public JSerializer()
        {
            _serializer = new JsonSerializer();
        }

        private JsonSerializer _serializer;        

        public object Deserialize(string json, Type type)
        {
            StringReader sr = new StringReader(json);
            JsonReader jr = new JsonReader(sr);

            return _serializer.Deserialize(jr, type);
        }

        public string Serialize(object value)
        {
            StringWriter sw = new StringWriter();
            _serializer.Serialize(sw, value);

            return sw.ToString();
        }        

        public Dictionary<string, string> Deserialize(string json, int paramCount)
        {
            Dictionary<string,string> args = new Dictionary<string, string>(paramCount);            
            var sr = new StringReader(json);
            var jd = new JsonReader(sr);
            var length = json.Length / 2;   

            int depth = -1;
            string property = string.Empty;
            string value = string.Empty;
            JsonToken lastToken = JsonToken.None;
            StringBuilder sb = null;

            while (jd.Read())
            {
                var token = jd.TokenType;
                switch (token)
                {
                    case JsonToken.StartObject:
                        depth++;
                        if (depth == 1)
                        {
                            sb = new StringBuilder(length);
                            sb.Append("{");
                        }
                        else if (depth > 1)
                        {
                            sb.Append("{");
                        }
                        lastToken = token;
                        break;

                    case JsonToken.EndObject:
                        depth--;

                        if (depth == 0)
                        {
                            sb.Append("}");
                            value = sb.ToString();

                            args.Add(property, value);
                        }
                        else if (depth > 0)
                        {
                            sb.Append("}");
                        }
                        lastToken = token;
                        break;

                    case JsonToken.PropertyName:

                        if (depth == 0)
                        {
                            property = jd.Value.ToString();
                        }
                        else
                        {
                            if (lastToken != JsonToken.StartObject)
                                sb.Append(",");

                            sb.Append("\"");
                            sb.Append(jd.Value as string);
                            sb.Append("\"");
                            sb.Append(":");
                        }
                        lastToken = token;
                        break;
                    case JsonToken.String:
                        value = jd.Value as string;
                        if (depth == 0)
                        {
                            args.Add(property, value);
                        }
                        else
                        {
                            sb.Append("\"");
                            sb.Append(jd.Value);
                            sb.Append("\"");
                        }
                        lastToken = token;
                        break;
                    case JsonToken.Integer:
                    case JsonToken.Boolean:
                    case JsonToken.Float:
                    case JsonToken.Null:
                    case JsonToken.Undefined:
                        value = jd.Value.ToString();
                        if (depth == 0)
                        {
                            args.Add(property, value);
                        }
                        else
                        {
                            sb.Append(jd.Value);
                        }
                        lastToken = token;
                        break;
                }
            }

            return args;
        }


        /*
         * 
         * public object[] MapParameters(Dictionary<string,string> args, ParameterInfo[] parameters)
        {
            int length = parameters.Length;
            var values = new object[length];

            for (int i = 0; i < length; i++)
            {
                var p = parameters[i];
                var name = p.Name;
                var type = p.ParameterType;

                var typeCode = Type.GetTypeCode(type);
                object value = null;

                switch (typeCode)
                {
                    case TypeCode.Object:
                        value = Deserialize(args[name], type);
                        break;
                    default:
                        value = args[name].ConvertTo(typeCode);//UString.ConvertTo(args[name], typeCode);
                        break;
                }
                values[i] = value;
            }

            return values;
        }

        public object[] MapParameters(Stream inputStream, ParameterInfo[] info)
        {
            var list = Deserialize(inputStream, info.Length);
            return MapParameters(list, info);
        }
        public Dictionary<string, string> Deserialize(string json, int size, int sbSize)
        {
            var args = new Dictionary<string, string>(size);
            var sr = new StringReader(json);
            var jd = new JsonReader(sr);


            int depth = -1;
            string property = string.Empty;
            string value = string.Empty;
            JsonToken lastToken = JsonToken.None;
            StringBuilder sb = null;

            while (jd.Read())
            {
                var token = jd.TokenType;
                switch (token)
                {
                    case JsonToken.StartObject:
                        depth++;
                        if (depth == 1)
                        {
                            sb = new StringBuilder(sbSize);
                            sb.Append("{");
                        }
                        else if (depth > 1)
                        {
                            sb.Append("{");
                        }
                        lastToken = token;
                        break;

                    case JsonToken.EndObject:
                        depth--;

                        if (depth == 0)
                        {
                            sb.Append("}");
                            value = sb.ToString();

                            args.Add(property, value);
                        }
                        else if (depth > 0)
                        {
                            sb.Append("}");
                        }
                        lastToken = token;
                        break;

                    case JsonToken.PropertyName:

                        if (depth == 0)
                        {
                            property = jd.Value.ToString();
                        }
                        else
                        {
                            if (lastToken != JsonToken.StartObject)
                                sb.Append(",");

                            sb.Append("\"");
                            sb.Append(jd.Value as string);
                            sb.Append("\"");
                            sb.Append(":");
                        }
                        lastToken = token;
                        break;
                    case JsonToken.String:
                        value = jd.Value as string;
                        if (depth == 0)
                        {
                            args.Add(property, value);
                        }
                        else
                        {
                            sb.Append("\"");
                            sb.Append(jd.Value);
                            sb.Append("\"");
                        }
                        lastToken = token;
                        break;
                    case JsonToken.Integer:
                    case JsonToken.Boolean:
                    case JsonToken.Float:
                    case JsonToken.Null:
                    case JsonToken.Undefined:
                        value = jd.Value.ToString();
                        if (depth == 0)
                        {
                            args.Add(property, value);
                        }
                        else
                        {
                            sb.Append(jd.Value);
                        }
                        lastToken = token;
                        break;
                }
            }

            return args;
        }
         * */
    }
}
